# LeetCode 125、验证回文串

# 一、题目描述

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

**说明:**本题中,我们将空字符串定义为有效的回文串。

示例 1:

输入: "A man, a plan, a canal: Panama"
输出: true
解释:"amanaplanacanalpanama" 是回文串

示例 2:

输入: "race a car"
输出: false
解释:"raceacar" 不是回文串

提示:

  • 1 <= s.length <= 2 * 105
  • 字符串 s 由 ASCII 字符组成

# 二、题目解析

具体操作如下:

1、 设置左右两个指针,分别指向字符串的开头位置和结束位置。

img

2、移动和观察者两个指针所指向元素之间的关系。

3、如果 left 指向的元素不是字母、也不是数字,那么可以忽略掉这个元素,即让 left 向右移动。

4、如果 right 指向的元素不是字母、也不是数字,那么可以忽略掉这个元素,即让 right 向左移动。

5、进过 3、4 操作后,要么 left 和 right 相遇了,跳出循环;要么 left 和 right 还没有相遇,并且它们都是指向字母或者数字。

6、只需要判断一下 left 和 right 指向的元素值是否相同就行,如果不相同,直接返回 false。

img

7、否则,继续让两个指针向内移动,left 向右移动,right 向左移动。

8、最后,没有得到 false 的答案就说明是回文串,返回 true

# 三、参考代码

# 1、Java 代码

// 登录 AlgoMooc 官网获取更多算法图解
// https://www.algomooc.com
// 作者:程序员吴师兄
// 微信:wzb_3377
// 代码有看不懂的地方一定要私聊咨询吴师兄呀
// 验证回文串(LeetCode 125):https://leetcode.cn/problems/valid-palindrome/submissions/
class Solution {
    public boolean isPalindrome(String s) {

        // 设置左右两个指针
        int left = 0 ;
        
        int right = s.length() - 1 ;


        // 移动和观察者两个指针所指向元素之间的关系
        while (left < right) {
            
            // 这里的逻辑有点像快速排序的代码

            // 如果 left 指向的元素不是字母、也不是数字
            // 那么可以忽略掉这个元素,即让 left 向右移动
            while (left < right && !Character.isLetterOrDigit(s.charAt(left))) {
                // left 向右移动
                left++;

            }

            // 如果 right 指向的元素不是字母、也不是数字
            // 那么可以忽略掉这个元素,即让 right 向左移动
            while (left < right && !Character.isLetterOrDigit(s.charAt(right))) {
                // right 向左移动
                right--;

            }

            // 来到这里时
            // 要么 left 和 right 相遇了,跳出循环
            // 要么 left 和 right 还没有相遇,并且它们都是指向字母或者数字
            if (left < right) {

                // 只需要判断一下 left 和 right 指向的元素值是否相同就行
                // 题目说明 可以忽略字母的大小写
                if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
                    // 不相同就直接说明不是回文串
                    return false;
                }

                // 否则,继续让两个指针向内移动

                // left 向右移动
                left++;

                // right 向左移动
                right--;
            }
        }

        // 最后,没有得到 false 的答案就说明是回文串,返回 true
        return true;
    }
}

# 2、C++ 代码

class Solution {
public:
    bool isPalindrome(string s) {
        // 设置左右两个指针
        int left = 0 ;
        
        int right = s.size() - 1 ;


        // 移动和观察者两个指针所指向元素之间的关系
        while (left < right) {
            
            // 这里的逻辑有点像快速排序的代码

            // 如果 left 指向的元素不是字母、也不是数字
            // 那么可以忽略掉这个元素,即让 left 向右移动
            while (left < right && !isalnum(s[left])) {
                // left 向右移动
                left++;

            }

            // 如果 right 指向的元素不是字母、也不是数字
            // 那么可以忽略掉这个元素,即让 right 向左移动
            while (left < right && !isalnum(s[right])) {
                // right 向左移动
                right--;

            }

            // 来到这里时
            // 要么 left 和 right 相遇了,跳出循环
            // 要么 left 和 right 还没有相遇,并且它们都是指向字母或者数字
            if (left < right) {

                // 只需要判断一下 left 和 right 指向的元素值是否相同就行
                // 题目说明 可以忽略字母的大小写
                if (tolower(s[left]) != tolower(s[right])) {
                    // 不相同就直接说明不是回文串
                    return false;
                }

                // 否则,继续让两个指针向内移动

                // left 向右移动
                left++;

                // right 向左移动
                right--;
            }
        }

        // 最后,没有得到 false 的答案就说明是回文串,返回 true
        return true;

    }
};

# 3、Python 代码

class Solution:
    def isPalindrome(self, s: str) -> bool:
        # 设置左右两个指针
        left = 0 
        
        right = len(s) - 1 


        # 移动和观察者两个指针所指向元素之间的关系
        while left < right : 
            
            # 这里的逻辑有点像快速排序的代码

            # 如果 left 指向的元素不是字母、也不是数字
            # 那么可以忽略掉这个元素,即让 left 向右移动
            while  left < right and not s[left].isalnum():
                # left 向右移动
                left += 1

            

            # 如果 right 指向的元素不是字母、也不是数字
            # 那么可以忽略掉这个元素,即让 right 向左移动
            while left < right and not s[right].isalnum():
                # right 向左移动
                right -= 1

        

            # 来到这里时
            # 要么 left 和 right 相遇了,跳出循环
            # 要么 left 和 right 还没有相遇,并且它们都是指向字母或者数字
            if left < right :

                # 只需要判断一下 left 和 right 指向的元素值是否相同就行
                # 题目说明 可以忽略字母的大小写
                if s[left].lower() != s[right].lower():
                    # 不相同就直接说明不是回文串
                    return False
                

                # 否则,继续让两个指针向内移动

                # left 向右移动
                left += 1

                # right 向左移动
                right -= 1
    
        # 最后,没有得到 false 的答案就说明是回文串,返回 true
        return True
class Solution:
    def isPalindrome(self, s: str) -> bool:

        # isalnum() 方法检测字符串是否由字母和数字组成
        # 转换为字符串数组的形式
        xArray = "".join(ch.lower() for ch in s if ch.isalnum())

        # 左边索引的位置在 0 
        left = 0

        # 右边索引的位置在 len(xArray) - 1
        right = len(xArray) - 1

        # 两个索引向内移动
        # left 向右移动
        # right 向左移动
        while left <= right:
            # 判断这两个元素值是否相同
            if xArray[left] != xArray[right]:
                # 如果不同,直接返回 False
                return  False

            # 否则,left 向右移动
            left += 1
        
            # right 向左移动
            right -= 1
        
        return True

# 四、复杂度分析

  • 时间复杂度:O(|s|),其中 |s| 是字符串 s 的长度。
  • 空间复杂度:O(1)。